#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#include "ring_fifo.h"

//对比
//cmp tmpfile/read_file tmpfile/write_file

pthread_t thd_rd, thd_wr;

static void* task_read(void *para);
static void* task_write(void *para);
static uint32_t get_tick_count(void);

struct ring_fifo_t *ring_fifo;

uint8_t buf[1u << 23];

#define MIN_TEST_DATA_SIZE    0
#define MAX_TEST_DATA_SIZE    345679
//测试数据长度
#define WR_TEST_DATA_SIZE (rand() % (MAX_TEST_DATA_SIZE - MIN_TEST_DATA_SIZE + 1) + MIN_TEST_DATA_SIZE)

#define MIN_WRITE_TIMERS    15000
#define MAX_WRITE_TIMERS    15001
//写次数
#define WR_TIMERS (rand() % (MIN_WRITE_TIMERS - MIN_WRITE_TIMERS + 1) + MIN_WRITE_TIMERS)

FILE *f_write, *f_read;

uint32_t thread_write_over;

int main(int argc, char *argv[])
{
    /* 初始化环形缓冲区 */
    ring_fifo = ring_fifo_init(buf, sizeof(buf), RF_TYPE_FRAME);
    if(NULL == ring_fifo)
    {
        printf("ring_fifo_init error\r\n");

        return EXIT_FAILURE;
    }

#if 0
    uint32_t wr_len, real_wr_len;
    uint8_t test_write_data[8];
    uint32_t rd_len;
    uint8_t test_read_data[32];

    printf("%s\n", ring_fifo_is_full(ring_fifo) ? "full" : "not full");
    printf("%s\n", ring_fifo_is_empty(ring_fifo) ? "empty" : "not empty");
    printf("avail: %u\n", ring_fifo_avail(ring_fifo));
    printf("count: %u\n", ring_fifo_count(ring_fifo));

    /* 填充数据 */
    for(uint32_t i = 0;i < sizeof(test_write_data);i++)
    {
        test_write_data[i] = i & 0xff;
    }
    wr_len = sizeof(test_write_data);
    real_wr_len = ring_fifo_write(ring_fifo, test_write_data, wr_len);
    printf("real_wr_len: %u\n", real_wr_len);

    printf("%s\n", ring_fifo_is_full(ring_fifo) ? "full" : "not full");
    printf("%s\n", ring_fifo_is_empty(ring_fifo) ? "empty" : "not empty");
    printf("avail: %u\n", ring_fifo_avail(ring_fifo));
    printf("count: %u\n", ring_fifo_count(ring_fifo));

    rd_len = ring_fifo_read(ring_fifo, test_read_data, sizeof(test_read_data));
    printf("rd_len: %u\n", rd_len);

    printf("%s\n", ring_fifo_is_full(ring_fifo) ? "full" : "not full");
    printf("%s\n", ring_fifo_is_empty(ring_fifo) ? "empty" : "not empty");
    printf("avail: %u\n", ring_fifo_avail(ring_fifo));
    printf("count: %u\n", ring_fifo_count(ring_fifo));
#else
    /* 创建并打开写文件 */
    f_write = fopen("tmpfile/write_file", "w+");
    if(!f_write)
    {
        perror("fopen write_file");

        return EXIT_FAILURE;
    }
    /* 创建并打开读文件 */
    f_read = fopen("tmpfile/read_file", "w+");
    if(!f_read)
    {
        perror("fopen read_file");

        return EXIT_FAILURE;
    }

    pthread_create(&thd_rd, NULL, task_read, NULL);
    pthread_create(&thd_wr, NULL, task_write, NULL);

    /* 等待写线程结束 */
    pthread_join(thd_wr, NULL);
    /* 标记写线程已结束 */
    thread_write_over = 1;
    /* 等待读线程结束 */
    pthread_join(thd_rd, NULL);

    fclose(f_write);
    fclose(f_read);
#endif
    ring_fifo_destroy(ring_fifo);

    return 0;
}

static uint64_t read_success_count;
void write_rd_file(const void *buf, uint32_t len)
{
    size_t size;
    uint32_t retry_cnt;

    if(0 == len)
    {
        return ;
    }

    read_success_count += len;
    retry_cnt = 0;
    do {
        size = fwrite(buf, len, 1, f_read);
        if(size >= 1)
        {
            break;
        }
    }while(++retry_cnt < 3);
    if(retry_cnt >= 3)
    {
        printf("read thread[%lu] write file failed..\r\n", pthread_self());
    }
}

__attribute__((used))
void* task_read(void *para)
{
    uint32_t rd_len;
    uint8_t test_read_data[MAX_TEST_DATA_SIZE];

    printf("enter read thread[%lu]\n", pthread_self());

    for(;;)
    {
        rd_len = ring_fifo_read(ring_fifo, test_read_data, sizeof(test_read_data));
        if(0 != rd_len)
        {
            write_rd_file(test_read_data, rd_len);
        }
        else
        {
            if(1 == thread_write_over)
            {
                break;
            }
        }

        //模拟数据处理
        // usleep(100);
    }

    printf("\r\nthread[%lu] read_success_count:%lu\r\n", pthread_self(), read_success_count);
    printf("thread[%lu] task_read exit..\r\n", pthread_self());

    return (void *)0;
}

static uint64_t write_success_count;
void write_wr_file(void *buf, uint32_t len)
{
    size_t size;
    uint32_t retry_cnt;

    if(0 == len)
    {
        return ;
    }

    write_success_count += len;
    retry_cnt = 0;
    do {
        size = fwrite(buf, len, 1, f_write);
        if(size >= 1)
        {
            break;
        }
    }while(++retry_cnt < 3);
    if(retry_cnt >= 3)
    {
        printf("write thread[%lu] write file failed..\r\n", pthread_self());
    }
}

__attribute__((used))
void* task_write(void *para)
{
    uint32_t wr_len, real_wr_len;
    uint8_t test_write_data[MAX_TEST_DATA_SIZE];

    printf("enter write thread[%lu]\n", pthread_self());

    /* 填充随机数据 */
    for(uint32_t i = 0;i < sizeof(test_write_data);i++)
    {
        test_write_data[i] = rand() % (0x39 - 0x30 + 1) + 0x30;
        // usleep(1000);
    }

    srand(get_tick_count());
    for(uint32_t i = 0;i < WR_TIMERS;i++)
    {
        srand(get_tick_count());
        wr_len = WR_TEST_DATA_SIZE;

        real_wr_len = ring_fifo_write(ring_fifo, test_write_data, wr_len);
        if(0 == real_wr_len)
        {
            continue;
        }
        write_wr_file(test_write_data, real_wr_len);
        if(real_wr_len != wr_len)
        {
            // printf("fifo is full.\n");
        }

        //模拟数据接收
        // usleep(1);
    }

    printf("\r\nthread[%lu] write_success_count:%lu\r\n", pthread_self(), write_success_count);
    printf("thread[%lu] task_write exit..\r\n", pthread_self());

    sleep(1);

    return (void *)0;
}

uint32_t get_tick_count(void)
{
    struct timespec tp;

    clock_gettime(CLOCK_MONOTONIC, &tp);

    return tp.tv_nsec;
}
